package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/service/model"
	"bgs/util"
	"context"
	"encoding/json"
	"fmt"
	"time"
)

func transformToRealPrizeProduct(
	o sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow,
	mapOfActivityProductAttributeDefs model.AttributeDefMapOfProduct,
	activitySkus []*model.ActivitySkuWithProductOrProductSpecInfo,
) *model.RealPrizeProduct {
	activitySkusOfTheActivityProduct := util.Filter(util.ToGenericSlice(activitySkus), func(v interface{}) bool {
		sku := v.(*model.ActivitySkuWithProductOrProductSpecInfo)
		return sku.ProductID == o.ProductID
	})

	return &model.RealPrizeProduct{
		ProductID:     o.ProductID,
		ProductName:   o.ProductName,
		ProductPics:   o.ProductPics,
		PriceMin:      o.PriceMin,
		PriceMax:      o.PriceMax,
		Quantity:      o.Quantity,
		AttributeDefs: mapOfActivityProductAttributeDefs[o.ProductID],
		SkuPtrs:       mapToRealPrizeSku(activitySkusOfTheActivityProduct, transformToRealPrizeSku),
	}
}

func mapToRealPrizeProduct(
	list []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow,
	mapOfActivityProductAttributeDefs model.AttributeDefMapOfProduct,
	skus []*model.ActivitySkuWithProductOrProductSpecInfo,
	f func(sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow,
		model.AttributeDefMapOfProduct,
		[]*model.ActivitySkuWithProductOrProductSpecInfo,
	) *model.RealPrizeProduct,
) []*model.RealPrizeProduct {
	r := make([]*model.RealPrizeProduct, len(list))
	for i, item := range list {
		r[i] = f(item, mapOfActivityProductAttributeDefs, skus)
	}
	return r
}

func transformToRealPrizeSku(v interface{}) *model.RealPrizeSku {
	o := v.(*model.ActivitySkuWithProductOrProductSpecInfo)
	return &model.RealPrizeSku{
		ProductID:     o.ProductID,
		ProductSpecID: o.ProductSpecID,
		StorageKey:    o.StorageKey,
		Price:         o.Price,
		SkuName:       o.SkuName,
	}
}

func mapToRealPrizeSku(list []interface{}, f func(interface{}) *model.RealPrizeSku) []*model.RealPrizeSku {
	r := make([]*model.RealPrizeSku, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

type InitNewActivityUserAwardExtInfoParam struct {
	ActivityPtr  *sqlc.Activity
	UserPtr      *sqlc.User
	AwardType    sqlc.EmAwardType
	awardInfoPtr *model.AwardInfo
}

func (s *ActivityServiceImp) initNewActivityUserAwardExtInfo(ctx context.Context, q *sqlc.Queries, p InitNewActivityUserAwardExtInfoParam) (extInfoPtr *model.ActivityUserAwardExtInfo, err error) {
	cBrand := s.cacher.FetchBrand(p.ActivityPtr.BrandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", p.ActivityPtr.BrandID)
		return
	}
	awardType := p.AwardType
	extInfoPtr = &model.ActivityUserAwardExtInfo{
		BrandName:      cBrand.Name,
		BrandTel:       cBrand.Tel,
		ActivityName:   p.ActivityPtr.Name,
		ActivityTel:    p.ActivityPtr.Tel,
		WinnerNickName: p.UserPtr.NickName.String,
		WinnerPic:      p.UserPtr.Pic.String,
		WinnerTel:      p.UserPtr.Mobile.String,
		AwardType:      string(awardType),
		AwardInfo:      *p.awardInfoPtr,
	}
	if awardType == sqlc.EmAwardTypeRealPrize {
		var activityWarehouseInfoPtr *model.ActivityWarehouseInfo
		activityWarehouseInfoPtr, err = s.initNewActivityWarehouseInfo(ctx, q, p.ActivityPtr)
		if err != nil {
			return
		}
		extInfoPtr.DeliveryModes = activityWarehouseInfoPtr.DeliveryModes
		extInfoPtr.ShippingFee = activityWarehouseInfoPtr.ShippingFee
		extInfoPtr.WarehouseID = activityWarehouseInfoPtr.WarehouseID
		extInfoPtr.WarehouseInfo = activityWarehouseInfoPtr.Warehouse
	}
	return
}

func (s *ActivityServiceImp) initNewActivityWarehouseInfo(ctx context.Context, q *sqlc.Queries, activityPtr *sqlc.Activity) (activityWarehouseInfoPtr *model.ActivityWarehouseInfo, err error) {

	cActivity := s.cacher.FetchActivity(activityPtr.ID)
	if cActivity == nil {
		err = fmt.Errorf("not found activity:%d", activityPtr.ID)
		return
	}

	if len(cActivity.WarehouseIDs) != 1 {
		err = fmt.Errorf("too many activity warehouseIDs:%d,%v", activityPtr.ID, cActivity.WarehouseIDs)
		return
	}

	deliveryModes := util.MapToString(util.ToGenericSlice(cActivity.DeliveryModes), func(v interface{}) string {
		val := v.(sqlc.EmDeliveryMode)
		return string(val)
	})
	shippingFee := cActivity.UnifiedShippingFee
	warehouseID := cActivity.WarehouseIDs[0]
	cWarehouse := s.cacher.FetchWarehouse(warehouseID)
	if cWarehouse == nil {
		err = fmt.Errorf("not found warehouse of warehouseID:%d", warehouseID)
		return
	}
	warehouseInfo := model.WarehouseInfo{
		Name:    cWarehouse.Name,
		Address: cWarehouse.Address,
		Lat:     cWarehouse.Lat,
		Lng:     cWarehouse.Lng,
		Linkman: cWarehouse.Linkman,
		Tel:     cWarehouse.Tel,
	}

	activityWarehouseInfoPtr = &model.ActivityWarehouseInfo{
		WarehouseID:   warehouseID,
		DeliveryModes: deliveryModes,
		ShippingFee:   shippingFee,
		Warehouse:     warehouseInfo,
	}
	return
}

type CreateUserAwardsSinceCheckIn1Param struct {
	ActivityPtr          *sqlc.Activity
	UserID               int64
	CheckIn1InfoPtr      *model.CheckIn1Info
	CheckIn1ProcedurePtr *model.CheckIn1Procedure
}

// CreateUserAwardsSinceCheckIn1 签到1创建活动用户奖励
func (s *ActivityServiceImp) CreateUserAwardsSinceCheckIn1(ctx context.Context, q *sqlc.Queries, p CreateUserAwardsSinceCheckIn1Param) (userAwards []*model.ActivityUserAwardExtInfo, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	user, err := q.GetUser(ctx, p.UserID)
	if err != nil {
		return
	}
	userPtr := &user

	isReachMax := p.CheckIn1ProcedurePtr.ContinuousCheckInCount >= p.CheckIn1InfoPtr.MaxContinuous

	now := time.Now()

	coins := model.BCoin(util.UInt32IfThenElse(isReachMax, uint32(p.CheckIn1InfoPtr.AwardReachMaxCoins), uint32(p.CheckIn1InfoPtr.AwardOnceCoins)))

	awardBCoinCheckIn1OnceExtInfoPtr, err := s.initNewActivityUserAwardExtInfo(ctx, q, InitNewActivityUserAwardExtInfoParam{
		ActivityPtr: p.ActivityPtr,
		UserPtr:     userPtr,
		AwardType:   sqlc.EmAwardTypeBCoin,
		awardInfoPtr: &model.AwardInfo{
			Coins: coins,
		},
	})
	if err != nil {
		return
	}
	// 单次签到1必然加5个星币
	awardBCoinCheckIn1OnceExtInfoBytes, marshalErr := json.Marshal(awardBCoinCheckIn1OnceExtInfoPtr)
	if marshalErr != nil {
		err = marshalErr
		return
	}
	_, err = q.CreateActivityUserAward(ctx, sqlc.CreateActivityUserAwardParams{
		BrandID:             p.ActivityPtr.BrandID,
		ActivityID:          p.ActivityPtr.ID,
		RootActivityID:      p.ActivityPtr.RootID,
		ParentActivityID:    p.ActivityPtr.ParentID,
		UserID:              p.UserID,
		NeedRedeemByMannual: false,
		RedeemDeadlineAt:    now,
		AwardType:           string(sqlc.EmAwardTypeBCoin),
		ExtInfo:             awardBCoinCheckIn1OnceExtInfoBytes,
	})
	if err != nil {
		return
	}
	userAwards = append(userAwards, awardBCoinCheckIn1OnceExtInfoPtr)
	slog.Infof("签到1创建活动用户奖励(单次签到获取星币) ok")

	if p.CheckIn1ProcedurePtr.ContinuousCheckInCount >= p.CheckIn1InfoPtr.MaxContinuous {
		slog.Infof("4、连签数(%d)达到最大连签数(%d),获取连签奖励", p.CheckIn1ProcedurePtr.ContinuousCheckInCount, p.CheckIn1InfoPtr.MaxContinuous)

		// 签到活动库存弱管控,在兑奖时选择规格并检测实时库存,中奖时仅记录可兑换奖品
		normalActivityProductWithProductInfos, normalActivitySkus, fetchErr := s.FetchNormalProductAndSkus(ctx, q, p.ActivityPtr.ID)
		if fetchErr != nil {
			err = fetchErr
			return
		}

		mapOfActivityProductAttributeDefs, fetchErr := s.FetchMapOfProductAttributeDefsByActivityID(ctx, q, p.ActivityPtr.ID)
		if fetchErr != nil {
			err = fetchErr
			return
		}

		realPrizePtrs := mapToRealPrizeProduct(normalActivityProductWithProductInfos, mapOfActivityProductAttributeDefs, normalActivitySkus, transformToRealPrizeProduct)

		awardRealPrizeCheckIn1ReachMaxContinuousExtInfoPtr, initErr := s.initNewActivityUserAwardExtInfo(ctx, q, InitNewActivityUserAwardExtInfoParam{
			ActivityPtr: p.ActivityPtr,
			UserPtr:     userPtr,
			AwardType:   sqlc.EmAwardTypeRealPrize,
			awardInfoPtr: &model.AwardInfo{
				RealPrizePtrs: realPrizePtrs,
			},
		})
		if initErr != nil {
			err = initErr
			return
		}
		awardRealPrizeCheckIn1ReachMaxContinuousExtInfoBytes, marshalErr := json.Marshal(awardRealPrizeCheckIn1ReachMaxContinuousExtInfoPtr)
		if marshalErr != nil {
			err = marshalErr
			return
		}
		redeemDeadlineAt := now.AddDate(0, 0, 7)
		_, err = q.CreateActivityUserAward(ctx, sqlc.CreateActivityUserAwardParams{
			BrandID:             p.ActivityPtr.BrandID,
			ActivityID:          p.ActivityPtr.ID,
			RootActivityID:      p.ActivityPtr.RootID,
			ParentActivityID:    p.ActivityPtr.ParentID,
			UserID:              p.UserID,
			NeedRedeemByMannual: true,
			RedeemDeadlineAt:    redeemDeadlineAt,
			AwardType:           string(sqlc.EmAwardTypeRealPrize),
			ExtInfo:             awardRealPrizeCheckIn1ReachMaxContinuousExtInfoBytes,
		})
		if err != nil {
			return
		}
		userAwards = append(userAwards, awardRealPrizeCheckIn1ReachMaxContinuousExtInfoPtr)
		slog.Infof("签到1创建活动用户奖励(达到最大连签数获取实物奖品) ok")
	}

	slog.Infof("签到1创建活动用户奖励 ok")
	return
}

type CreateUserAwardsSinceLottery1Param struct {
	ActivityPtr             *sqlc.Activity
	UserID                  int64
	WinActivityProductPtr   *sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow
	WinPrizeDistributionPtr *model.PrizeDistribution
	ActivitySkus            []*model.ActivitySkuWithProductOrProductSpecInfo
}

// CreateUserAwardsSinceLottery1 抽奖1创建活动用户奖励
func (s *ActivityServiceImp) CreateUserAwardsSinceLottery1(ctx context.Context, q *sqlc.Queries, p CreateUserAwardsSinceLottery1Param) (userAwards []*model.ActivityUserAwardExtInfo, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	user, err := q.GetUser(ctx, p.UserID)
	if err != nil {
		return
	}

	userPtr := &user

	cBrand := s.cacher.FetchBrand(p.ActivityPtr.BrandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", p.ActivityPtr.BrandID)
		return
	}

	now := time.Now()

	var realPrizePtrs []*model.RealPrizeProduct
	mapOfActivityProductAttributeDefs, fetchErr := s.FetchMapOfProductAttributeDefsByProductID(ctx, q, p.WinActivityProductPtr.ProductID)
	if fetchErr != nil {
		err = fetchErr
		return
	}

	realPrizePtrs = append(realPrizePtrs, transformToRealPrizeProduct(*p.WinActivityProductPtr, mapOfActivityProductAttributeDefs, p.ActivitySkus))
	awardRealPrizeLottery1WinExtInfoPtr, err := s.initNewActivityUserAwardExtInfo(ctx, q, InitNewActivityUserAwardExtInfoParam{
		ActivityPtr: p.ActivityPtr,
		UserPtr:     userPtr,
		AwardType:   sqlc.EmAwardTypeRealPrize,
		awardInfoPtr: &model.AwardInfo{
			Prize:         p.WinPrizeDistributionPtr.Prize,
			RealPrizePtrs: realPrizePtrs,
		},
	})
	if err != nil {
		return
	}
	awardRealPrizeLottery1WinExtInfoBytes, err := json.Marshal(awardRealPrizeLottery1WinExtInfoPtr)
	if err != nil {
		return
	}
	redeemDeadlineAt := now.AddDate(0, 0, 7)
	_, err = q.CreateActivityUserAward(ctx, sqlc.CreateActivityUserAwardParams{
		BrandID:             p.ActivityPtr.BrandID,
		ActivityID:          p.ActivityPtr.ID,
		RootActivityID:      p.ActivityPtr.RootID,
		ParentActivityID:    p.ActivityPtr.ParentID,
		UserID:              p.UserID,
		NeedRedeemByMannual: true,
		RedeemDeadlineAt:    redeemDeadlineAt,
		AwardType:           string(sqlc.EmAwardTypeRealPrize),
		ExtInfo:             awardRealPrizeLottery1WinExtInfoBytes,
	})
	if err != nil {
		return
	}
	userAwards = append(userAwards, awardRealPrizeLottery1WinExtInfoPtr)

	slog.Infof("抽奖1创建活动用户奖励(抽中) ok")
	return
}

// UpdateUserAwardSinceRedeemAwardOrderCreated 兑奖订单创建成功后更新兑奖记录
func (s *ActivityServiceImp) UpdateUserAwardSinceRedeemAwardOrderCreated(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(orderPtr.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal order ext_info失败:%s", err.Error())
		return
	}

	awardID := orderExtInfo.AwardID

	if awardID == 0 {
		err = fmt.Errorf("兑换奖励订单没有记录AwardID")
		return
	}

	_, err = q.UpdateActivityUserAwardOrderInfoSinceOrderCreated(ctx, sqlc.UpdateActivityUserAwardOrderInfoSinceOrderCreatedParams{
		ID:                awardID,
		OrderID:           sqlc.NInt64FromInt64(orderPtr.ID),
		OrderSerialNumber: sqlc.NSFromString(orderPtr.SerialNumber),
	})
	if err != nil {
		return
	}

	slog.Infof("兑奖订单创建成功后更新兑奖记录 ok")
	return
}

// RedeemUserAwardsSinceOrderPayed 兑奖订单支付成功后更新兑奖记录
func (s *ActivityServiceImp) UpdateUserAwardSinceRedeemAwardOrderPayed(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(orderPtr.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal order ext_info失败:%s", err.Error())
		return
	}

	awardID := orderExtInfo.AwardID

	if awardID == 0 {
		err = fmt.Errorf("兑换奖励订单没有记录AwardID")
		return
	}

	_, err = q.UpdateActivityUserAwardRedeemSinceOrderPayed(ctx, awardID)
	if err != nil {
		return
	}

	slog.Infof("兑奖订单支付成功后更新兑奖记录 ok")
	return
}


func (s *ActivityServiceImp) AggrRealPrizeProductCount(ctx context.Context, q *sqlc.Queries, activityID int64, winProductID int64) (wined int64, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	container := &model.ActivityUserAwardExtInfoSearchPath{
		SearchPathPtr: &model.AwardInfoSearchPath{
			RealPrizeContainerPtrs: []*model.RealPrizeProductSearchPath{
				{ProductID: winProductID},
			},
		},
	}
	productIdPathBytes, err := json.Marshal(container)
	if err != nil {
		return
	}

	wined, err = q.AggrActivityUserAwardsForRealPrizeByActivityIDAndProductID(ctx, sqlc.AggrActivityUserAwardsForRealPrizeByActivityIDAndProductIDParams{
		ActivityID: activityID,
		ProductIDPath: productIdPathBytes,
	})
	if err != nil {
		return
	}
	slog.Infof("aggrRealPrizeProductCount(%d) ok", wined)
	return
}